package com.yinxing.framework.convert;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.yinxing.framework.annotation.Json;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Spring参数转换器(用于处理JSON字符串)
 * 例如前台post请求参数 user={name:jack,age=18}
 * 后台方法 insert(@Json User user)会把{name:jack, age=18}转换为user对象
 */
public class JsonParamConvert implements HandlerMethodArgumentResolver {

	private ObjectMapper objectMapper;

	public JsonParamConvert(ObjectMapper objectMapper) {
		Assert.notNull(objectMapper, "objectMapper must not be null");
		this.objectMapper = objectMapper;
	}

	/**
	 * 参数前面需要打@Json注解的才转换
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.getParameterAnnotation(Json.class) != null;
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer, 
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		final String parameterName = parameter.getParameterName();
		final String jsonString = webRequest.getParameter(parameterName);
		final Class<?> parameterClass = parameter.getParameterType();

		if(!StringUtils.hasLength(jsonString)) {
			return null;
 		}

		Object convertObject = null;
		JavaType javaType = null;
		
		if(Collection.class.isAssignableFrom(parameterClass)) {
			Type type = parameter.getGenericParameterType();
			if(type instanceof ParameterizedType) {
				ParameterizedType parameterizedType = (ParameterizedType) type;
				Type elementType = parameterizedType.getActualTypeArguments()[0];
				Class<?> elementClass;
				if(elementType instanceof ParameterizedType) {
					elementClass = (Class<?>) ((ParameterizedType) elementType).getRawType();
				} else {
					elementClass = (Class<?>) elementType;
				}
				javaType = constructCollectionType((Class<? extends Collection<?>>)parameterClass, elementClass);
			}
		} 
		else if (parameterClass.isArray()) {
			Class<?> elementType = parameterClass.getComponentType();
			javaType = constructArrayType(elementType);
		} 
		else if(parameterClass.isAssignableFrom(Map.class)) {
			javaType = constructType(LinkedHashMap.class);
		} 
		else {
			javaType = constructType(parameterClass);
		}
		
		if(javaType != null) {
			convertObject = objectMapper.readValue(jsonString, javaType);
		}
		
		return convertObject;
	}
	
	private JavaType constructCollectionType(Class<? extends Collection<?>> collectionClass, Class<?> elementClass) {
		return TypeFactory.defaultInstance().constructCollectionType(collectionClass, elementClass);
	}
	
	private JavaType constructArrayType(Class<?> elementType) {
		return TypeFactory.defaultInstance().constructArrayType(elementType);
	}
	
	private JavaType constructType(Class<?> type) {
		return TypeFactory.defaultInstance().constructType(type);
	}

}
